#include "MetaParser.h"

namespace GxMetaParser
{

	Token & Token::operator = (const Token & tk)
	{
		Str = tk.Str;
		Position = tk.Position;
		Type = tk.Type;
		return *this;
	}

	MetaParser::MetaParser(GxMetaParser::ILexer *l, GxMetaParser::Grammar *g)
	{
		lexer = l;
		grammar = g;
	}

	int MetaParser::GetClientObjectCount()
	{
		return stack.Count();
	}

	RefPtr<ClientSymbolObject> MetaParser::GetClientObject(int id)
	{
		return stack[id].ClientObject;
	}

	bool MetaParser::Parse()
	{
		if (!grammar->ParseTable /*|| grammar->Errors.Count()*/)
		{
			throw IllegalGrammarException();
		}
		bool result = true;
		Array<int> stateStack;
		stateStack.SetCapacity(1024);
		stateStack.Add(0);
		bool shouldExit = false;
		int terminalCount = grammar->GetTerminalSymbolCount();
		while (1)
		{
			int state = stateStack.End();
			Token tk = lexer->GetNextToken();
			Action & act = grammar->ParseTable->Actions[state][tk.Type];
			switch (act.Type)
			{
			case Action::Shift:
				{
					SymbolObject nobj;
					nobj.TokenData = tk;
					nobj.SymbolType = tk.Type;
					stack.Add(nobj);
					stateStack.Add(act.ID);
					lexer->ReadNextToken();
				}
				break;
			case Action::Reduct:
				{
					ProductionRule * rule = grammar->Rules[act.ID].operator ->();
					int c = rule->Symbols.Count();
					ReductionEventArgs e;
					for (int i=stack.Count()-c; i<stack.Count(); i++)
					{
						SymbolObject * obj = new SymbolObject();
						obj->ClientObject = stack[i].ClientObject;
						obj->SymbolType = stack[i].SymbolType;
						obj->TokenData = stack[i].TokenData;
						e.SymbolObjects.Add(obj);
					}
					stack.Delete(stack.Count()-c, c);
					
					e.Rule = rule;
					rule->OnReduction(e);
					SymbolObject obj;
					obj.ClientObject = e.ReturnObject;
					obj.SymbolType = rule->NonTerminalID;
					stack.Add(obj);
					stateStack.Delete(stateStack.Count()-c, c);
					state = stateStack.End();
					state = grammar->ParseTable->Goto[state][rule->NonTerminalID-terminalCount];
					stateStack.Add(state);
				}
				break;
			case Action::Accept:
				{
					return result;
				}
			case Action::Error:
				{
					ParserErrorArgs e;
					e.State = state;
					e.Symbol = tk.Type;
					OnError(e);
					result = false;
					if (tk.Type == grammar->GetEndSymbolID())
						shouldExit = true;
					else
					{
						ProductionRule * rule = 0;
						bool found = false;
						while (stateStack.Count()>1)
						{
							stateStack.Delete(stateStack.Count()-1);
							state = stateStack.End();
							found = false;
							for (int i=0; i<grammar->ItemSets[state]->Items.Count(); i++)
							{
								Item & item = grammar->ItemSets[state]->Items[i];
								if (item.Position<grammar->Rules[item.ProductionRule]->Symbols.Count()
									&& grammar->Rules[item.ProductionRule]->Symbols[item.Position] == grammar->GetErrorSymbolID())
								{
									rule = grammar->Rules[item.ProductionRule].operator ->();
									found = true;
									break;
								}
							}
							if (found)
								break;

						}
						if (found)
						{
							Action & act = grammar->ParseTable->Actions[state][grammar->GetErrorSymbolID()];
							if (act.Type == Action::Accept)
							{
								return false;
							}
							else if (act.Type == Action::Reduct)
							{
								ReductionEventArgs e;
								RefPtr<SymbolObject> sobj = new SymbolObject();
								sobj->SymbolType = grammar->GetErrorSymbolID();
								sobj->TokenData.Type = -1;
								e.Rule = rule;
								e.SymbolObjects.Add(sobj);
								rule->OnReduction(e);
								SymbolObject obj;
								obj.ClientObject = e.ReturnObject;
								obj.SymbolType = rule->NonTerminalID;
								stack.Add(obj);
								stateStack.Delete(stateStack.Count()-1);
								state = stateStack.End();
								stateStack.Add(grammar->ParseTable->Goto[state][rule->NonTerminalID]);
								state = stateStack.End();
							}
							else if (act.Type == Action::Shift)
							{
								stateStack.Add(act.ID);
								state = act.ID;
								SymbolObject sobj;
								sobj.SymbolType = grammar->GetErrorSymbolID();
								sobj.TokenData.Type = -1;
								stack.Add(sobj);

							}
							if (!shouldExit)
							{
								Token token = lexer->GetNextToken();
								while (token.Type != grammar->GetEndSymbolID()
									&& grammar->ParseTable->Actions[state][token.Type].Type == Action::Error)
								{
									token = lexer->ReadNextToken();
								}
							}
						}
						else
							shouldExit = true;
					}
				}
				break;
			}
			if (shouldExit)
				break;
		}
		return false;
	}
}